package it.antonio.ilpizze.db.core;

import it.antonio.ilpizze.db.DbException;
import it.antonio.ilpizze.db.Locker;
import it.antonio.ilpizze.db.exceptions.LockingRequiredException;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LockManager implements Locker{
		
	private LockManagerDelegate delegate;
	
	private StatusManager statusManager;
	private boolean multiThreading;
	
	public LockManager(StatusManager statusManager) {
		super();
		delegate = new SingleThreadLockManagerDelegate();
		this.statusManager = statusManager;
		this.multiThreading = false;
	}

	public void multiThreading(boolean multiThreading) {
		if(multiThreading == true){
			delegate = new MultiThreadLockManagerDelegate();
		} else {
			delegate = new SingleThreadLockManagerDelegate();
		}
		this.multiThreading = multiThreading;
	}
	
	public void checkMultiThreadingWrite() {
		if(multiThreading && delegate.isWriteLock() == false){
			throw new LockingRequiredException("WRITE");
		}
	}
	public void checkMultiThreadingRead() {
		if(multiThreading && (delegate.isReadLock() == false && delegate.isWriteLock() == false)) {
			throw new LockingRequiredException("READ");
		}
	}
	@Override
	public void readLock() {
		statusManager.checkStatus(Status.OPEN, Status.TRANSACTION);
		delegate.readLock();
	}

	@Override
	public void readUnlock() {
		statusManager.checkStatus(Status.OPEN, Status.TRANSACTION);
		delegate.readUnlock();
	}

	@Override
	public void writeLock() {
		statusManager.checkStatus(Status.OPEN, Status.TRANSACTION);
		delegate.writeLock();
	}

	@Override
	public void writeUnlock() {
		statusManager.checkStatus(Status.OPEN, Status.TRANSACTION);
		delegate.writeUnlock();		
	}

	public boolean isWriteLock() {
		return delegate.isWriteLock();
	}
	public boolean isReadLock() {
		return delegate.isReadLock();
	}

	private interface LockManagerDelegate {

		void readLock();

		void readUnlock();

		void writeLock();

		void writeUnlock();

		boolean isWriteLock();

		boolean isReadLock();
		
	}
	
	private class SingleThreadLockManagerDelegate implements LockManagerDelegate{

		@Override
		public void readLock() {
			throw new DbException("Multi Threading not enabled");
		}

		@Override
		public void readUnlock() {
			throw new DbException("Multi Threading not enabled");
		}

		@Override
		public void writeLock() {
			throw new DbException("Multi Threading not enabled");
		}

		@Override
		public void writeUnlock() {
			throw new DbException("Multi Threading not enabled");
		}

		@Override
		public boolean isWriteLock() {
			return false;
		}

		@Override
		public boolean isReadLock() {
			return false;
		}

		
		
	}
	private class MultiThreadLockManagerDelegate implements LockManagerDelegate{
		private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
		private boolean writeLocked;
		private boolean readLocked;
		
		public MultiThreadLockManagerDelegate() {
			this.writeLocked = false;
			this.readLocked = false;
		}

		@Override
		public void readLock() {
			lock.readLock().lock();
			readLocked = true;
		}

		@Override
		public void readUnlock() {
			lock.readLock().unlock();
			readLocked = false;
		}

		@Override
		public void writeLock() {
			lock.writeLock().lock();
			writeLocked = true;
		}

		@Override
		public void writeUnlock() {
			lock.writeLock().unlock();
			writeLocked = false;
		}

		@Override
		public boolean isWriteLock() {
			return writeLocked;
		}

		@Override
		public boolean isReadLock() {
			return readLocked;
		}
	}

}
